﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using System.ComponentModel;
using FourKillEnginePrototype.Properties;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Media;
using System.Reflection;

namespace FourKill.Engine
{
    public class FourkillEngine : Control
    {
        public enum FirstPersonViewControlState { AnimateGame, BeginPauseDarken, PauseDarken }

        private Dictionary<string, SoundPlayer> _soundPlayers = new Dictionary<string, SoundPlayer>();
        private bool _musicPlaying = false;
        private Timer _timer;
        private FirstPersonViewControlState _controlState = FirstPersonViewControlState.AnimateGame;
        private bool[] _directions = new bool[8];
        private IContainer _components;
        private PaintingContext _paintingContext = new PaintingContext();
        private Brush _skyBrush = Brushes.SkyBlue;
        private Brush _groundBrush = new SolidBrush(Color.FromArgb(25, 97, 11));
        private List<IGameObject> _gameObjects = new List<IGameObject>();
        private List<IPaintable> _underlayPainters = new List<IPaintable>();
        private List<IPaintable> _overlayPainters = new List<IPaintable>();

        private IViewPort _viewPort = new ViewPort(6, Utility._90, Utility._45);

        private ICamera _camera = new Camera
        {
            Bearing = Utility.Radians(90),
            Width = 3,
            Height = 6.25,
            X = 30,
            Y = 10
        };

        public ICamera Camera
        {
            get { return _camera; }
            set { _camera = value; }
        }

       /// <summary>
        /// Initializes a new instance of the <see cref="FourkillEngine"/> class.
        /// </summary>
        public FourkillEngine()
        {

            // Test Texture

            InitializeComponent();

            //This turns off internal double buffering of all custom GDI+ drawing
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);
            this.SetStyle(ControlStyles.ResizeRedraw, true);
        }

        public void Start()
        {
            LoadSounds();
            _timer.Enabled = true;
        }

        public void Stop()
        {
            _timer.Enabled = false;
        }

        /// <summary>
        /// Gets the game objects.
        /// </summary>
        public List<IGameObject> GameObjects { get { return _gameObjects; } }
        public List<IPaintable> UnderlayPainters { get { return _underlayPainters; } }
        public List<IPaintable> OverlayPainters { get { return _overlayPainters; } }

        /// <summary>
        /// Draws the first person view.
        /// </summary>
        /// <param name="g">The g.</param>
        private void DrawFirstPersonView(Graphics g)
        {
            this._viewPort.PixelSize = this.Size; // Draw Grass
            using (Bitmap frontLayerBmp = new Bitmap(this.Size.Width, this.Size.Height))
            {
                using (var frontLayer = Graphics.FromImage(frontLayerBmp))
                {
                    frontLayer.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
                    frontLayer.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
                    frontLayer.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
                    frontLayer.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
                    frontLayer.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel;

                    _paintingContext.Camera = _camera;
                    _paintingContext.ViewPort = _viewPort;
                    _paintingContext.BackLayer = g;
                    _paintingContext.FrontLayer = frontLayer;

                    try
                    {
                        foreach (var painter in _underlayPainters)
                        {
                            _paintingContext.GameObject = null;
                            painter.Paint(_paintingContext);
                        }

                        _gameObjects.ForEach(s => s.UpdateLocation(_paintingContext));
                        foreach (var obj in _gameObjects.OrderByDescending(e => e.LocationRelativeToCamera.Distance))
                        {
                            _paintingContext.GameObject = obj;
                            obj.Painter.Paint(_paintingContext);

                            //For debugging
                            if (obj is IGameObject && obj is IPath)
                            {
                                //IPath path = obj as IPath;
                                //IGameObject go = obj as IGameObject;
                                //var rel = go.LocationRelativeToCamera;
                            }
                        }

                        foreach (var painter in _overlayPainters)
                        {
                            _paintingContext.GameObject = null;
                            painter.Paint(_paintingContext);
                        }
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine("***********************************");
                        System.Diagnostics.Debug.WriteLine(ex.Message + "\r\n" + ex.StackTrace);
                        System.Diagnostics.Debugger.Break();
                    }

                    g.DrawImage(frontLayerBmp, this.ClientRectangle);
                }
            }
        }

        private Bitmap _pausedScreen;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="pe"></param>
        protected override void OnPaint(PaintEventArgs pe)
        {
            if (_controlState != FirstPersonViewControlState.PauseDarken)
            {
                if (_controlState == FirstPersonViewControlState.BeginPauseDarken)
                {
                    _controlState = FirstPersonViewControlState.PauseDarken;
                    _pausedScreen = new Bitmap(this.Size.Width, this.Size.Height);

                    using (Graphics graphics = Graphics.FromImage(_pausedScreen))
                    {
                        DrawFirstPersonView(graphics);
                    }
                    Utility.AdjustBrightnessMatrix(_pausedScreen, -150);
                }
                else
                {
                    DrawFirstPersonView(pe.Graphics);
                }
            }
            else
            {
                pe.Graphics.DrawImage(_pausedScreen, 0, 0);
            }
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_timer != null)
                    _timer.Dispose();
                if (_soundPlayers != null && _soundPlayers.Count > 0)
                    foreach (SoundPlayer soundPlayer in _soundPlayers.Values) soundPlayer.Dispose();
            }
            base.Dispose(disposing);
        }

        protected override bool IsInputKey(System.Windows.Forms.Keys keyData)
        {
            if ((keyData == Keys.Up) ||
                (keyData == Keys.Down) ||
                (keyData == Keys.Left) ||
                (keyData == Keys.Right) ||
                (keyData == Keys.Space)) return true;
            else return base.IsInputKey(keyData);
        }

        void Controls_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.W:
                    _directions[0] = true;
                    break;
                case Keys.Down:
                case Keys.S:
                    _directions[1] = true;
                    break;
                case Keys.Left:
                case Keys.Q:
                    _directions[2] = true;
                    break;
                case Keys.Right:
                case Keys.E:
                    _directions[3] = true;
                    break;
                case Keys.D:
                    _directions[4] = true;
                    break;
                case Keys.A:
                    _directions[5] = true;
                    break;
                case Keys.Space:
                    _directions[6] = true;
                    break;
                case Keys.C:
                    _directions[7] = true;
                    break;
            }
        }

        void Controls_KeyUp(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.W:
                    _directions[0] = false;
                    break;
                case Keys.Down:
                case Keys.S:
                    _directions[1] = false;
                    break;
                case Keys.Left:
                case Keys.Q:
                    _directions[2] = false;
                    break;
                case Keys.Right:
                case Keys.E:
                    _directions[3] = false;
                    break;
                case Keys.D:
                    _directions[4] = false;
                    break;
                case Keys.A:
                    _directions[5] = false;
                    break;
                case Keys.Space:
                    _directions[6] = false;
                    break;
                case Keys.C:
                    _directions[7] = false;
                    break;
                case Keys.D1:
                    Utility.CurrentPlacementRule = Utility.PlacementRule.TwoDimensionalFlat;
                    break;
                case Keys.D2:
                    Utility.CurrentPlacementRule = Utility.PlacementRule.TwoDimensional;
                    break;
                case Keys.D3:
                    Utility.CurrentPlacementRule = Utility.PlacementRule.ThreeDimensional;
                    break;
                case Keys.P:
                    if (_musicPlaying) _soundPlayers["music"].Stop();
                    _musicPlaying = false;
                    if (_controlState != FirstPersonViewControlState.PauseDarken) _soundPlayers["chimes"].Play();
                    else
                    {
                        _soundPlayers["music"].PlayLooping();
                        _musicPlaying = true;
                    }
                    _controlState = _controlState == FirstPersonViewControlState.PauseDarken ? FirstPersonViewControlState.AnimateGame : FirstPersonViewControlState.BeginPauseDarken;
                    break;
                case Keys.M:
                    if (_musicPlaying) 
                    {
                        _soundPlayers["music"].Stop();_musicPlaying = false; 
                    }
                    else
                    {
                        _soundPlayers["music"].Dispose();
                        _soundPlayers["music"] = new SoundPlayer(Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("FourKillEnginePrototype.Resources.loops.loop" + _rand.Next(1, 9) + ".wav"));

                        _soundPlayers["music"].PlayLooping();
                        _musicPlaying = true;
                    }
                    break;
            }
        }

        private void LoadSounds()
        {
            if (_soundPlayers.Count == 0)
            {
                _soundPlayers.Add("chimes", new SoundPlayer(Environment.SystemDirectory + @"\..\Media\chimes.wav"));
                _soundPlayers.Add("music", new SoundPlayer(Assembly.GetAssembly(this.GetType()).GetManifestResourceStream("FourKillEnginePrototype.Resources.loops.loop"+_rand.Next(1,9)+".wav")));

                //_soundPlayers["music"].PlayLooping();
                _musicPlaying = false;
            }
        }


        void Timer_Tick(object sender, EventArgs e)
        {
            if (_controlState != FirstPersonViewControlState.PauseDarken)
            {
                if (_directions[0]) this._camera.Move(2);
                if (_directions[1]) this._camera.Move(-2);
                if (_directions[2]) this._camera.Turn(Utility.Radians(5));
                if (_directions[3]) this._camera.Turn(Utility.Radians(-5));
                if (_directions[4]) this._camera.SideStep(2);
                if (_directions[5]) this._camera.SideStep(-2);
                if (_directions[6]) this._camera.Z++;
                if (_directions[7]) if (this._camera.Z + 1 >= 0) this._camera.Z -= 1;

                foreach (var obj in GameObjects) obj.Tick();
            }

            this.Invalidate();
        }
        private Random _rand = new Random();

        /// <summary>
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this._components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FourkillEngine));
            this.SuspendLayout();

            // 
            // FirstPersonViewControl2
            // 
            this.Name = "FirstPersonView";
            this.Size = new System.Drawing.Size(200, 100);
            this.ResumeLayout(false);

            this.KeyDown += new KeyEventHandler(Controls_KeyDown);
            this.KeyUp += new KeyEventHandler(Controls_KeyUp);

            _timer = new Timer();
            _timer.Interval = 50;
            _timer.Tick += new EventHandler(Timer_Tick);
        }

    }
}
